using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using The_Last_Knights.Event;
using The_Last_Knights.GUI;
using The_Last_Knights.GameScreen;
using The_Last_Knights.Helper;
using The_Last_Knights.Helper.Xml;

namespace The_Last_Knights
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class RpgGame : Game
    {
        public delegate bool GlobalEvent(object src, object sender, string EventID, object wParam, object lParam); 

        GraphicsDeviceManager _graphics;
        SpriteBatch _spriteBatch;
        ScreenManager _screenManager;
        private bool _isFullScreen;

        public RpgGame()
        {
            _graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            IsMouseVisible = true;
            _isFullScreen = false;
            ConfigureGraphicsManager();
        }

        private void ConfigureGraphicsManager()
        {
#if XBOX360
            _graphics.PreferredBackBufferWidth = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width;
            _graphics.PreferredBackBufferHeight = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height;
            _graphics.IsFullScreen = true;
#else
            _graphics.PreferredBackBufferWidth = 1024;
            _graphics.PreferredBackBufferHeight = 768;
            _graphics.IsFullScreen = _isFullScreen;
            GameSettings.Resolution = new Vector2(1024, 768);
#endif      
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            ReaderEntity.Game = this;
            EventEntity._Game = this;
            ScreenManager.Initialize(this);  
            EventRoutingManager.GetInstance(this).Initialize();          
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            _spriteBatch = new SpriteBatch(GraphicsDevice);

            _screenManager = ScreenManager.GetInstance();
            Components.Add(_screenManager);
            Components.Add(EventRoutingManager.GetInstance(this));

            Services.AddService(typeof(SpriteBatch), _spriteBatch);
            Services.AddService(typeof(ScreenManager), _screenManager);

            //Tao Background Screen
            StaticScreen.LoadTitle(this, (XmlReader.Read(GameAssertPath.XML_PATH + "MasterPage.xml")[0] as string));
            var backgroundScreen = XmlReader.Read(GameAssertPath.XML_PATH + "BackgroundScreen/BackgroundScreenDefinition.xml")[0] as BackgroundScreen;
            var optionScreen = XmlReader.Read(GameAssertPath.XML_PATH + "OptionScreen/OptionScreenDefinition.xml")[0] as OptionScreen;
            if (optionScreen != null) _screenManager.AddScreen(optionScreen.Id, optionScreen);

            var creditScreen = XmlReader.Read(GameAssertPath.XML_PATH + "MasterySelection/MasterySelectionDefinition.xml")[0] as CreditScreen;
            if (creditScreen != null) _screenManager.AddScreen(creditScreen.Id, creditScreen);
                        
            if (backgroundScreen != null)
            {
                backgroundScreen.FormMenu.Container.Opacity = 0.1f;
                _screenManager.AddScreen(backgroundScreen.Id, backgroundScreen);                
            }
            //Chuyen man hinh hien tai sang backgroundScreen
            _screenManager.SwitchScreen(backgroundScreen.Id);            
            
            SetEvent();
        }

        /// <summary>
        /// Ham nay se set event cho toan bo he thong
        /// </summary>
        private void SetEvent()
        {
            List<object> eventPackeds = XmlReader.Read(GameAssertPath.XML_PATH + "GUIEvent.xml");
            //Chi load vua du assemply, tranh 1 class duoc load nhieu lan -> lang phi bo nho
            Dictionary<string, Assembly> assemblies = new Dictionary<string, Assembly>();
            //Quan lys cac thuc the de sau nay dua vao trong he thong su dung
            Dictionary<string, object[]> eventEntities = new Dictionary<string, object[]>();            

            //Doi moi goi packed, can phan tach du lieu, 
            //EventID la ID cua GameEntity, 
            //con cac PreCond, PosCond la cac event ma GameEntity se lang nghe
            foreach (var e in eventPackeds)
            {
                EventPacked eventPacked = e as EventPacked;
                string controlID = eventPacked.ControlId;
                string file = eventPacked.ExecutionFile;
                string fileName = Environment.CurrentDirectory + "\\" + file;
                Assembly asm = null;
                if (!assemblies.ContainsKey(fileName))
                    asm = Assembly.LoadFile(fileName);
                else
                    asm = assemblies[fileName];
                string className = file.Replace(@"Event/", "").Replace(".dll", "") + "." + eventPacked.ClassName;                
                Type[] types = asm.GetTypes();
                IEvent eventEntity = null;
                foreach (var type in types)
                {
                    if (type.FullName == className)
                    {
                        if (!eventEntities.ContainsKey(className))
                        {
                            eventEntity = (IEvent)Activator.CreateInstance(type);
                            (eventEntity as EventEntity).MustFinished = eventPacked.PreCond[0];
                            (eventEntity as EventEntity).MustRunning = eventPacked.PreCond[1];                            
                            (eventEntity as EventEntity).MustNotRun = eventPacked.PreCond[2];
                            object[] tmp = new object[2];
                            tmp[0] = eventPacked.EventId;
                            tmp[1] = eventEntity;
                            eventEntities.Add(className, tmp);                            
                        }
                        else
                        {
                            eventEntity = eventEntities[className][1] as EventEntity;
                        }
                    }
                }

                if (eventEntity != null)
                {
                    if (controlID != null && controlID != "")
                    {
                        (GameEntityDictionary.GetValue(controlID) as GameEntity).Event += eventEntity.OnEvent;
                        (GameEntityDictionary.GetValue(controlID) as GameEntity).EventId = eventPacked.EventId;
                    }
                    else
                    {
                        for (int i = 0; i < 3; i++)
                        {
                            foreach (var id in eventPacked.PreCond[i])
                            {
                                EventRoutingManager.GetInstance(this).RegisterListener(eventEntity as EventEntity, id);    
                            }
                        }
                    }
                }                
            }
            foreach (var eventEntity in eventEntities)
            {
                object[] tmp = eventEntity.Value;
                EventEntityDictionary.Add(tmp[0] as string, tmp[1] as EventEntity);
            }            
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            KeyboardState kbs = Keyboard.GetState();
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || kbs.IsKeyDown(Keys.Escape))
            {                
                Exit();
            }

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            _screenManager.Draw(gameTime);
            
            base.Draw(gameTime);
        }
    }
}
